home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / non-ANSI / c-client / bezerk.c next >
Encoding:
C/C++ Source or Header  |  1993-07-20  |  67.9 KB  |  2,287 lines

  1. /*
  2.  * Program:    Berkeley mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    20 December 1989
  13.  * Last Edited:    20 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. /* Dedication:
  38.  *  This file is dedicated with affection to those Merry Marvels of Musical
  39.  * Madness . . .
  40.  *  ->  The Incomparable Leland Stanford Junior University Marching Band  <-
  41.  * who entertain, awaken, and outrage Stanford fans in the fact of repeated
  42.  * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
  43.  * HUSKY FEVER!!!].
  44.  *
  45.  */
  46.  
  47. #include <stdio.h>
  48. #include <ctype.h>
  49. #include <netdb.h>
  50. #include <errno.h>
  51. extern int errno;        /* just in case */
  52. #include <signal.h>
  53. #include "mail.h"
  54. #include "osdep.h"
  55. #include <pwd.h>
  56. #include <sys/file.h>
  57. #include <sys/stat.h>
  58. #include <sys/time.h>
  59. #include "bezerk.h"
  60. #include "rfc822.h"
  61. #include "misc.h"
  62.  
  63. /* Berkeley mail routines */
  64.  
  65.  
  66. /* Driver dispatch used by MAIL */
  67.  
  68. DRIVER bezerkdriver = {
  69.   "bezerk",            /* driver name */
  70.   (DRIVER *) NIL,        /* next driver */
  71.   bezerk_valid,            /* mailbox is valid for us */
  72.   bezerk_parameters,        /* manipulate parameters */
  73.   bezerk_find,            /* find mailboxes */
  74.   bezerk_find_bboards,        /* find bboards */
  75.   bezerk_find_all,        /* find all mailboxes */
  76.   bezerk_find_all_bboards,    /* find all bboards */
  77.   bezerk_subscribe,        /* subscribe to mailbox */
  78.   bezerk_unsubscribe,        /* unsubscribe from mailbox */
  79.   bezerk_subscribe_bboard,    /* subscribe to bboard */
  80.   bezerk_unsubscribe_bboard,    /* unsubscribe from bboard */
  81.   bezerk_create,        /* create mailbox */
  82.   bezerk_delete,        /* delete mailbox */
  83.   bezerk_rename,        /* rename mailbox */
  84.   bezerk_open,            /* open mailbox */
  85.   bezerk_close,            /* close mailbox */
  86.   bezerk_fetchfast,        /* fetch message "fast" attributes */
  87.   bezerk_fetchflags,        /* fetch message flags */
  88.   bezerk_fetchstructure,    /* fetch message envelopes */
  89.   bezerk_fetchheader,        /* fetch message header only */
  90.   bezerk_fetchtext,        /* fetch message body only */
  91.   bezerk_fetchbody,        /* fetch message body section */
  92.   bezerk_setflag,        /* set message flag */
  93.   bezerk_clearflag,        /* clear message flag */
  94.   bezerk_search,        /* search for message based on criteria */
  95.   bezerk_ping,            /* ping mailbox to see if still alive */
  96.   bezerk_check,            /* check for new messages */
  97.   bezerk_expunge,        /* expunge deleted messages */
  98.   bezerk_copy,            /* copy messages to another mailbox */
  99.   bezerk_move,            /* move messages to another mailbox */
  100.   bezerk_append,        /* append string message to mailbox */
  101.   bezerk_gc            /* garbage collect stream */
  102. };
  103.  
  104.                 /* prototype stream */
  105. MAILSTREAM bezerkproto = {&bezerkdriver};
  106.  
  107. /* Berkeley mail validate mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: our driver if name is valid, NIL otherwise
  110.  */
  111.  
  112. DRIVER *bezerk_valid (name)
  113.     char *name;
  114. {
  115.   char tmp[MAILTMPLEN];
  116.   return bezerk_isvalid (name,tmp) ? &bezerkdriver : NIL;
  117. }
  118.  
  119.  
  120. /* Berkeley mail test for valid mailbox
  121.  * Accepts: mailbox name
  122.  * Returns: T if valid, NIL otherwise
  123.  */
  124.  
  125. int bezerk_isvalid (name,tmp)
  126.     char *name;
  127.     char *tmp;
  128. {
  129.   int i,fd,ti,zn;
  130.   int ret = NIL;
  131.   char *s = tmp,*t;
  132.   struct stat sbuf;
  133.   struct hostent *host_name;
  134.   if (!lhostn) {        /* have local host yet? */
  135.     gethostname(tmp,MAILTMPLEN);/* get local host name */
  136.     lhostn = cpystr ((host_name = gethostbyname (tmp)) ?
  137.              host_name->h_name : tmp);
  138.   }
  139.                 /* INBOX is always accepted */
  140.   if (!strcmp (ucase (strcpy (tmp,name)),"INBOX")) return T;
  141.                 /* if file, get its status */
  142.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  143.       (stat (bezerk_file (tmp,name),&sbuf) == 0) &&
  144.       ((fd = open (tmp,O_RDONLY,NIL)) >= 0)) {
  145.     if (sbuf.st_size == 0) {     /* allow empty file if not .txt */
  146.       if ((i = strlen (tmp)) < 4 || strcmp (tmp + i - 4 ,".txt"))
  147.     return LONGT;
  148.     }
  149.     else if ((read (fd,tmp,MAILTMPLEN-1) >= 0) &&
  150.          (tmp[0] == 'F') && VALID (s,t,ti,zn)) ret = T;
  151.     close (fd);            /* close the file */
  152.   }
  153.   return ret;            /* return what we should */
  154. }
  155.  
  156. /* Berkeley manipulate driver parameters
  157.  * Accepts: function code
  158.  *        function-dependent value
  159.  * Returns: function-dependent return value
  160.  */
  161.  
  162. void *bezerk_parameters (function,value)
  163.     long function;
  164.     void *value;
  165. {
  166.   fatal ("Invalid bezerk_parameters function");
  167.   return NIL;
  168. }
  169.  
  170. /* Berkeley mail find list of mailboxes
  171.  * Accepts: mail stream
  172.  *        pattern to search
  173.  */
  174.  
  175. void bezerk_find (stream,pat)
  176.     MAILSTREAM *stream;
  177.     char *pat;
  178. {
  179.   void *s = NIL;
  180.   char *t,tmp[MAILTMPLEN];
  181.   while (t = sm_read (&s))    /* read subscription database */
  182.     if ((*t != '{') && (*t != '*') &&
  183.     strcmp (t,"INBOX") && pmatch (t,pat) && bezerk_isvalid (t,tmp))
  184.       mm_mailbox (t);
  185. }
  186.  
  187.  
  188. /* Berkeley mail find list of bboards
  189.  * Accepts: mail stream
  190.  *        pattern to search
  191.  */
  192.  
  193. void bezerk_find_bboards (stream,pat)
  194.     MAILSTREAM *stream;
  195.     char *pat;
  196. {
  197.   void *s = NIL;
  198.   char *t,tmp[MAILTMPLEN];
  199.   while (t = sm_read (&s))    /* read subscription database */
  200.     if ((*t == '*') && (t[1] != '{') &&
  201.     pmatch (t+1,pat) && bezerk_isvalid (t+1,tmp))
  202.       mm_bboard (t+1);
  203. }
  204.  
  205. /* Berkeley mail find list of all mailboxes
  206.  * Accepts: mail stream
  207.  *        pattern to search
  208.  */
  209.  
  210. void bezerk_find_all (stream,pat)
  211.     MAILSTREAM *stream;
  212.     char *pat;
  213. {
  214.   DIR *dirp;
  215.   struct direct *d;
  216.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  217.   int i = 0;
  218.   char *s,*t;
  219.   if (s = strrchr (pat,'/')) {    /* directory specified in pattern? */
  220.     strncpy (file,pat,i = (++s) - pat);
  221.     file[i] = '\0';        /* tie off prefix */
  222.     t = bezerk_file (tmp,pat);    /* make fully-qualified file name */
  223.                 /* tie off directory name */
  224.     if (s = strrchr (t,'/')) *s = '\0';
  225.   }
  226.   else t = myhomedir ();    /* use home directory to search */
  227.   if (dirp = opendir (t)) {    /* now open that directory */
  228.     while (d = readdir (dirp)) {/* for each directory entry */
  229.       strcpy (file + i,d->d_name);
  230.       if (pmatch (file,pat) && (bezerk_isvalid (file,tmp))) mm_mailbox (file);
  231.     }
  232.     closedir (dirp);        /* flush directory */
  233.   }
  234. }
  235.  
  236. /* Berkeley mail find list of all bboards
  237.  * Accepts: mail stream
  238.  *        pattern to search
  239.  */
  240.  
  241. void bezerk_find_all_bboards (stream,pat)
  242.     MAILSTREAM *stream;
  243.     char *pat;
  244. {
  245.   DIR *dirp;
  246.   struct direct *d;
  247.   struct passwd *pw;
  248.   char tmp[MAILTMPLEN],file[MAILTMPLEN];
  249.   int i = 1;
  250.   char *s;
  251.   if (!((pw = getpwnam ("ftp")) && pw->pw_dir)) return;
  252.   file[0] = '*';        /* bboard designator */
  253.                 /* directory specified in pattern? */
  254.   if (s = strrchr (pat,'/')) strncpy (file + 1,pat,i += (++s) - pat);
  255.   file[i] = '\0';        /* tie off prefix */
  256.   sprintf (tmp,"%s/%s",pw->pw_dir,(file[1] == '/') ? file + 2 : file + 1);
  257.   if (dirp = opendir (tmp)) {    /* now open that directory */
  258.     while (d = readdir (dirp)) {/* for each directory entry */
  259.       strcpy (file + i,d->d_name);
  260.       if (pmatch (file + 1,pat) && (bezerk_isvalid (file,tmp)))
  261.     mm_bboard (file + 1);
  262.     }
  263.     closedir (dirp);        /* flush directory */
  264.   }
  265. }
  266.  
  267. /* Berkeley mail subscribe to mailbox
  268.  * Accepts: mail stream
  269.  *        mailbox to add to subscription list
  270.  * Returns: T on success, NIL on failure
  271.  */
  272.  
  273. long bezerk_subscribe (stream,mailbox)
  274.     MAILSTREAM *stream;
  275.     char *mailbox;
  276. {
  277.   char tmp[MAILTMPLEN];
  278.   return sm_subscribe (bezerk_file (tmp,mailbox));
  279. }
  280.  
  281.  
  282. /* Berkeley mail unsubscribe to mailbox
  283.  * Accepts: mail stream
  284.  *        mailbox to delete from subscription list
  285.  * Returns: T on success, NIL on failure
  286.  */
  287.  
  288. long bezerk_unsubscribe (stream,mailbox)
  289.     MAILSTREAM *stream;
  290.     char *mailbox;
  291. {
  292.   char tmp[MAILTMPLEN];
  293.   return sm_unsubscribe (bezerk_file (tmp,mailbox));
  294. }
  295.  
  296.  
  297. /* Berkeley mail subscribe to bboard
  298.  * Accepts: mail stream
  299.  *        bboard to add to subscription list
  300.  * Returns: T on success, NIL on failure
  301.  */
  302.  
  303. long bezerk_subscribe_bboard (stream,mailbox)
  304.     MAILSTREAM *stream;
  305.     char *mailbox;
  306. {
  307.   return NIL;            /* never valid for Bezerk */
  308. }
  309.  
  310.  
  311. /* Berkeley mail unsubscribe to bboard
  312.  * Accepts: mail stream
  313.  *        bboard to delete from subscription list
  314.  * Returns: T on success, NIL on failure
  315.  */
  316.  
  317. long bezerk_unsubscribe_bboard (stream,mailbox)
  318.     MAILSTREAM *stream;
  319.     char *mailbox;
  320. {
  321.   return NIL;            /* never valid for Bezerk */
  322. }
  323.  
  324. /* Berkeley mail create mailbox
  325.  * Accepts: MAIL stream
  326.  *        mailbox name to create
  327.  * Returns: T on success, NIL on failure
  328.  */
  329.  
  330. long bezerk_create (stream,mailbox)
  331.     MAILSTREAM *stream;
  332.     char *mailbox;
  333. {
  334.   char tmp[MAILTMPLEN];
  335.   int i,fd;
  336.                 /* must be a ".txt" file */
  337.   if ((i = strlen (mailbox)) > 4 && !strcmp (mailbox + i - 4 ,".txt")) {
  338.     mm_log ("Can't create mailbox: name must not end with .txt",ERROR);
  339.     return NIL;
  340.   }
  341.   if ((fd = open (bezerk_file (tmp,mailbox),O_WRONLY|O_CREAT|O_EXCL,0600))<0) {
  342.     sprintf (tmp,"Can't create mailbox %s: %s",mailbox,strerror (errno));
  343.     mm_log (tmp,ERROR);
  344.     return NIL;
  345.   }
  346.   close (fd);            /* close the file */
  347.   return T;            /* return success */
  348. }
  349.  
  350.  
  351. /* Berkeley mail delete mailbox
  352.  * Accepts: MAIL stream
  353.  *        mailbox name to delete
  354.  * Returns: T on success, NIL on failure
  355.  */
  356.  
  357. long bezerk_delete (stream,mailbox)
  358.     MAILSTREAM *stream;
  359.     char *mailbox;
  360. {
  361.   return bezerk_rename (stream,mailbox,NIL);
  362. }
  363.  
  364. /* Berkeley mail rename mailbox
  365.  * Accepts: MAIL stream
  366.  *        old mailbox name
  367.  *        new mailbox name (or NIL for delete)
  368.  * Returns: T on success, NIL on failure
  369.  */
  370.  
  371. long bezerk_rename (stream,old,new)
  372.     MAILSTREAM *stream;
  373.     char *old;
  374.     char *new;
  375. {
  376.   long ret = T;
  377.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
  378.   int fd,ld;
  379.                 /* get the c-client lock */
  380.   if ((ld = open (lockname (lock,bezerk_file (file,old)),O_RDWR|O_CREAT,0666))
  381.       < 0) {
  382.     sprintf (tmp,"Can't get lock for mailbox %s: %s",old,strerror (errno));
  383.     mm_log (tmp,ERROR);
  384.     return NIL;
  385.   }
  386.                 /* lock out other c-clients */
  387.   if (flock (ld,LOCK_EX|LOCK_NB)) {
  388.     close (ld);            /* couldn't lock, give up on it then */
  389.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  390.     mm_log (tmp,ERROR);
  391.     return NIL;
  392.   }
  393.                 /* lock out non c-client applications */
  394.   if ((fd = bezerk_lock (file,O_RDONLY,S_IREAD|S_IWRITE,lockx,LOCK_EX)) < 0) {
  395.     sprintf (tmp,"Can't lock mailbox %s: %s",old,strerror (errno));
  396.     mm_log (tmp,ERROR);
  397.     return NIL;
  398.   }
  399.                 /* do the rename or delete operation */
  400.   if (new ? rename (file,bezerk_file (tmp,new)) : unlink (file)) {
  401.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  402.          strerror (errno));
  403.     mm_log (tmp,ERROR);
  404.     ret = NIL;            /* set failure */
  405.   }
  406.   bezerk_unlock (fd,NIL,lockx);    /* unlock and close mailbox */
  407.   flock (ld,LOCK_UN);        /* release c-client lock lock */
  408.   close (ld);            /* close c-client lock */
  409.   unlink (lock);        /* and delete it */
  410.   return ret;            /* return success */
  411. }
  412.  
  413. /* Berkeley mail open
  414.  * Accepts: Stream to open
  415.  * Returns: Stream on success, NIL on failure
  416.  */
  417.  
  418. MAILSTREAM *bezerk_open (stream)
  419.     MAILSTREAM *stream;
  420. {
  421.   long i;
  422.   long retry = KODRETRY;    /* number of seconds to retry */
  423.   int fd;
  424.   char tmp[MAILTMPLEN];
  425.   struct stat sbuf;
  426.                 /* return prototype for OP_PROTOTYPE call */
  427.   if (!stream) return &bezerkproto;
  428.   if (LOCAL) {            /* close old file if stream being recycled */
  429.     bezerk_close (stream);    /* dump and save the changes */
  430.     stream->dtb = &bezerkdriver;/* reattach this driver */
  431.     mail_free_cache (stream);    /* clean up cache */
  432.   }
  433.   stream->local = fs_get (sizeof (BEZERKLOCAL));
  434.                 /* canonicalize the stream mailbox name */
  435.   bezerk_file (tmp,stream->mailbox);
  436.                 /* force readonly if bboard */
  437.   if (*stream->mailbox == '*') stream->readonly = T;
  438.   else {            /* canonicalize name */
  439.     fs_give ((void **) &stream->mailbox);
  440.     stream->mailbox = cpystr (tmp);
  441.   }
  442.   /* You may wonder why LOCAL->name is needed.  It isn't at all obvious from
  443.    * the code.  The problem is that when a stream is recycled with another
  444.    * mailbox of the same type, the driver's close method isn't called because
  445.    * it could be IMAP and closing then would defeat the entire point of
  446.    * recycling.  Hence there is code in the file drivers to call the close
  447.    * method such as what appears above.  The problem is, by this point,
  448.    * mail_open() has already changed the stream->mailbox name to point to the
  449.    * new name, and bezerk_close() needs the old name.
  450.    */
  451.   LOCAL->name = cpystr (tmp);    /* local copy for recycle case */
  452.                 /* build name of our lock file */
  453.   LOCAL->lname = cpystr (lockname (tmp,LOCAL->name));
  454.   LOCAL->ld = NIL;        /* no state locking yet */
  455.   LOCAL->filesize = 0;        /* initialize file information */
  456.   LOCAL->filetime = 0;
  457.   LOCAL->msgs = NIL;        /* no cache yet */
  458.   LOCAL->cachesize = 0;
  459.   LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1);
  460.   stream->sequence++;        /* bump sequence number */
  461.  
  462.   LOCAL->dirty = NIL;        /* no update yet */
  463.                 /* make lock for read/write access */
  464.   if (!stream->readonly) while (retry) {
  465.                 /* get a new file handle each time */
  466.     if ((fd = open (LOCAL->lname,O_RDWR|O_CREAT,0666)) < 0)
  467.       mm_log ("Can't open mailbox lock, access is readonly",WARN);
  468.                 /* can get the lock? */
  469.     if (flock (fd,LOCK_EX|LOCK_NB)) {
  470.       if (retry-- == KODRETRY) {/* no, first time through? */
  471.                 /* yes, get other process' PID */
  472.     if (!fstat (fd,&sbuf) && (i = min (sbuf.st_size,MAILTMPLEN)) &&
  473.         (read (fd,tmp,i) == i) && (i = atol (tmp))) {
  474.       kill (i,SIGUSR2);    /* send the Kiss Of Death */
  475.       sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
  476.       mm_log (tmp,WARN);
  477.     }
  478.     else retry = 0;        /* give up */
  479.       }
  480.       close (fd);        /* get a new handle next time around */
  481.       if (retry) sleep (1);    /* wait a second before trying again */
  482.       else mm_log ("Mailbox is open by another process, access is readonly",
  483.            WARN);
  484.     }
  485.     else {            /* got the lock, nobody else can alter state */
  486.       LOCAL->ld = fd;        /* note lock's fd */
  487.       chmod (LOCAL->lname,0666);/* make sure mode OK (don't use fchmod()) */
  488.                 /* note our PID in the lock */
  489.       sprintf (tmp,"%d",getpid ());
  490.       write (fd,tmp,(i = strlen (tmp))+1);
  491.       ftruncate (fd,i);        /* make sure tied off */
  492.       fsync (fd);        /* make sure it's available */
  493.       retry = 0;        /* no more need to try */
  494.     }
  495.   }
  496.  
  497.                 /* parse mailbox */
  498.   stream->nmsgs = stream->recent = 0;
  499.                 /* will we be able to get write access? */
  500.   if (LOCAL->ld && access (LOCAL->name,W_OK) && (errno == EACCES)) {
  501.     mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  502.     flock (LOCAL->ld,LOCK_UN);    /* release the lock */
  503.     close (LOCAL->ld);        /* close the lock file */
  504.     LOCAL->ld = NIL;        /* no more lock fd */
  505.     unlink (LOCAL->lname);    /* delete it */
  506.     fs_give ((void **) &LOCAL->lname);
  507.   }
  508.                 /* parse mailbox */
  509.   if ((fd = bezerk_parse (stream,tmp,LOCK_SH)) >= 0) {
  510.     bezerk_unlock (fd,stream,tmp);
  511.     mail_unlock (stream);
  512.   }
  513.   if (!LOCAL) return NIL;    /* failure if stream died */
  514.   stream->readonly = !LOCAL->ld;/* make sure upper level knows readonly */
  515.                 /* notify about empty mailbox */
  516.   if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
  517.   return stream;        /* return stream alive to caller */
  518. }
  519.  
  520. /* Berkeley mail close
  521.  * Accepts: MAIL stream
  522.  */
  523.  
  524. void bezerk_close (stream)
  525.     MAILSTREAM *stream;
  526. {
  527.   int fd;
  528.   int silent = stream->silent;
  529.   char lock[MAILTMPLEN];
  530.   if (LOCAL && LOCAL->ld) {    /* is stream alive? */
  531.     stream->silent = T;        /* note this stream is dying */
  532.                 /* lock mailbox and parse new messages */
  533.     if (LOCAL->dirty && (fd = bezerk_parse (stream,lock,LOCK_EX)) >= 0) {
  534.                 /* dump any changes not saved yet */
  535.       if (bezerk_extend    (stream,fd,"Close failed to update mailbox"))
  536.     bezerk_save (stream,fd);
  537.                 /* flush locks */
  538.       bezerk_unlock (fd,stream,lock);
  539.       mail_unlock (stream);
  540.     }
  541.     stream->silent = silent;    /* restore previous status */
  542.   }
  543.   bezerk_abort (stream);    /* now punt the file and local data */
  544. }
  545.  
  546.  
  547. /* Berkeley mail fetch fast information
  548.  * Accepts: MAIL stream
  549.  *        sequence
  550.  */
  551.  
  552. void bezerk_fetchfast (stream,sequence)
  553.     MAILSTREAM *stream;
  554.     char *sequence;
  555. {
  556.   return;            /* no-op for local mail */
  557. }
  558.  
  559.  
  560. /* Berkeley mail fetch flags
  561.  * Accepts: MAIL stream
  562.  *        sequence
  563.  */
  564.  
  565. void bezerk_fetchflags (stream,sequence)
  566.     MAILSTREAM *stream;
  567.     char *sequence;
  568. {
  569.   return;            /* no-op for local mail */
  570. }
  571.  
  572. /* Berkeley mail fetch structure
  573.  * Accepts: MAIL stream
  574.  *        message # to fetch
  575.  *        pointer to return body
  576.  * Returns: envelope of this message, body returned in body value
  577.  *
  578.  * Fetches the "fast" information as well
  579.  */
  580.  
  581. ENVELOPE *bezerk_fetchstructure (stream,msgno,body)
  582.     MAILSTREAM *stream;
  583.     long msgno;
  584.     BODY **body;
  585. {
  586.   ENVELOPE **env;
  587.   BODY **b;
  588.   STRING bs;
  589.   LONGCACHE *lelt;
  590.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  591.   long i = max (m->headersize,m->bodysize);
  592.   if (stream->scache) {        /* short cache */
  593.     if (msgno != stream->msgno){/* flush old poop if a different message */
  594.       mail_free_envelope (&stream->env);
  595.       mail_free_body (&stream->body);
  596.     }
  597.     stream->msgno = msgno;
  598.     env = &stream->env;        /* get pointers to envelope and body */
  599.     b = &stream->body;
  600.   }
  601.   else {            /* long cache */
  602.     lelt = mail_lelt (stream,msgno);
  603.     env = &lelt->env;        /* get pointers to envelope and body */
  604.     b = &lelt->body;
  605.   }
  606.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  607.     mail_free_envelope (env);    /* flush old envelope and body */
  608.     mail_free_body (b);
  609.     if (i > LOCAL->buflen) {    /* make sure enough buffer space */
  610.       fs_give ((void **) &LOCAL->buf);
  611.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  612.     }
  613.     INIT (&bs,mail_string,(void *) m->body,m->bodysize);
  614.                 /* parse envelope and body */
  615.     rfc822_parse_msg (env,body ? b : NIL,m->header,m->headersize,&bs,lhostn,
  616.               LOCAL->buf);
  617.   }
  618.   if (body) *body = *b;        /* return the body */
  619.   return *env;            /* return the envelope */
  620. }
  621.  
  622. /* Berkeley mail snarf message, only for Tenex driver
  623.  * Accepts: MAIL stream
  624.  *        message # to snarf
  625.  *        pointer to size to return
  626.  * Returns: message text in RFC822 format
  627.  */
  628.  
  629. char *bezerk_snarf (stream,msgno,size)
  630.     MAILSTREAM *stream;
  631.     long msgno;
  632.     long *size;
  633. {
  634.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  635.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  636.   if (((*size = m->headersize + m->bodysize) > LOCAL->buflen) ||
  637.       LOCAL->buflen > CHUNK) {    /* make sure stream can hold the text */
  638.                 /* fs_resize would do an unnecessary copy */
  639.     fs_give ((void **) &LOCAL->buf);
  640.     LOCAL->buf = (char *) fs_get((LOCAL->buflen = max (*size,(long) CHUNK))+1);
  641.   }
  642.                 /* copy the text */
  643.   if (m->headersize) memcpy (LOCAL->buf,m->header,m->headersize);
  644.   if (m->bodysize) memcpy (LOCAL->buf + m->headersize,m->body,m->bodysize);
  645.   LOCAL->buf[*size] = '\0';    /* tie off string */
  646.   return LOCAL->buf;
  647. }
  648.  
  649. /* Berkeley mail fetch message header
  650.  * Accepts: MAIL stream
  651.  *        message # to fetch
  652.  * Returns: message header in RFC822 format
  653.  */
  654.  
  655. char *bezerk_fetchheader (stream,msgno)
  656.     MAILSTREAM *stream;
  657.     long msgno;
  658. {
  659.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  660.                 /* copy the string */
  661.   return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->header,m->headersize);
  662. }
  663.  
  664.  
  665. /* Berkeley mail fetch message text (only)
  666.     body only;
  667.  * Accepts: MAIL stream
  668.  *        message # to fetch
  669.  * Returns: message text in RFC822 format
  670.  */
  671.  
  672. char *bezerk_fetchtext (stream,msgno)
  673.     MAILSTREAM *stream;
  674.     long msgno;
  675. {
  676.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  677.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  678.   if (!elt->seen) {        /* if message not seen */
  679.     elt->seen = T;        /* mark message as seen */
  680.                 /* recalculate Status/X-Status lines */
  681.     bezerk_update_status (m->status,elt);
  682.     LOCAL->dirty = T;        /* note stream is now dirty */
  683.   }
  684.   return strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,m->body,m->bodysize);
  685. }
  686.  
  687. /* Berkeley fetch message body as a structure
  688.  * Accepts: Mail stream
  689.  *        message # to fetch
  690.  *        section specifier
  691.  *        pointer to length
  692.  * Returns: pointer to section of message body
  693.  */
  694.  
  695. char *bezerk_fetchbody (stream,m,s,len)
  696.     MAILSTREAM *stream;
  697.     long m;
  698.     char *s;
  699.     unsigned long *len;
  700. {
  701.   BODY *b;
  702.   PART *pt;
  703.   unsigned long i;
  704.   char *base = LOCAL->msgs[m - 1]->body;
  705.   unsigned long offset = 0;
  706.   MESSAGECACHE *elt = mail_elt (stream,m);
  707.                 /* make sure have a body */
  708.   if (!(bezerk_fetchstructure (stream,m,&b) && b && s && *s &&
  709.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  710.   do {                /* until find desired body part */
  711.                 /* multipart content? */
  712.     if (b->type == TYPEMULTIPART) {
  713.       pt = b->contents.part;    /* yes, find desired part */
  714.       while (--i && (pt = pt->next));
  715.       if (!pt) return NIL;    /* bad specifier */
  716.                 /* note new body, check valid nesting */
  717.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  718.       offset = pt->offset;    /* get new offset */
  719.     }
  720.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  721.                 /* need to go down further? */
  722.     if (i = *s) switch (b->type) {
  723.     case TYPEMESSAGE:        /* embedded message */
  724.       offset = b->contents.msg.offset;
  725.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  726.     case TYPEMULTIPART:        /* multipart, get next section */
  727.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  728.     default:            /* bogus subpart specification */
  729.       return NIL;
  730.     }
  731.   } while (i);
  732.                 /* lose if body bogus */
  733.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  734.   if (!elt->seen) {        /* if message not seen */
  735.     elt->seen = T;        /* mark message as seen */
  736.                 /* recalculate Status/X-Status lines */
  737.     bezerk_update_status (LOCAL->msgs[m - 1]->status,elt);
  738.     LOCAL->dirty = T;        /* note stream is now dirty */
  739.   }
  740.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  741.               b->size.ibytes,b->encoding);
  742. }
  743.  
  744. /* Berkeley mail set flag
  745.  * Accepts: MAIL stream
  746.  *        sequence
  747.  *        flag(s)
  748.  */
  749.  
  750. void bezerk_setflag (stream,sequence,flag)
  751.     MAILSTREAM *stream;
  752.     char *sequence;
  753.     char *flag;
  754. {
  755.   MESSAGECACHE *elt;
  756.   long i;
  757.   short f = bezerk_getflags (stream,flag);
  758.   if (!f) return;        /* no-op if no flags to modify */
  759.                 /* get sequence and loop on it */
  760.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  761.     if ((elt = mail_elt (stream,i))->sequence) {
  762.                 /* set all requested flags */
  763.       if (f&fSEEN) elt->seen = T;
  764.       if (f&fDELETED) elt->deleted = T;
  765.       if (f&fFLAGGED) elt->flagged = T;
  766.       if (f&fANSWERED) elt->answered = T;
  767.                 /* recalculate Status/X-Status lines */
  768.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  769.       LOCAL->dirty = T;        /* note stream is now dirty */
  770.     }
  771. }
  772.  
  773.  
  774. /* Berkeley mail clear flag
  775.  * Accepts: MAIL stream
  776.  *        sequence
  777.  *        flag(s)
  778.  */
  779.  
  780. void bezerk_clearflag (stream,sequence,flag)
  781.     MAILSTREAM *stream;
  782.     char *sequence;
  783.     char *flag;
  784. {
  785.   MESSAGECACHE *elt;
  786.   long i;
  787.   short f = bezerk_getflags (stream,flag);
  788.   if (!f) return;        /* no-op if no flags to modify */
  789.                 /* get sequence and loop on it */
  790.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  791.     if ((elt = mail_elt (stream,i))->sequence) {
  792.                 /* clear all requested flags */
  793.       if (f&fSEEN) elt->seen = NIL;
  794.       if (f&fDELETED) elt->deleted = NIL;
  795.       if (f&fFLAGGED) elt->flagged = NIL;
  796.       if (f&fANSWERED) elt->answered = NIL;
  797.                 /* recalculate Status/X-Status lines */
  798.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  799.       LOCAL->dirty = T;        /* note stream is now dirty */
  800.     }
  801. }
  802.  
  803. /* Berkeley mail search for messages
  804.  * Accepts: MAIL stream
  805.  *        search criteria
  806.  */
  807.  
  808. void bezerk_search (stream,criteria)
  809.     MAILSTREAM *stream;
  810.     char *criteria;
  811. {
  812.   long i,n;
  813.   char *d;
  814.   search_t f;
  815.                 /* initially all searched */
  816.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  817.                 /* get first criterion */
  818.   if (criteria && (criteria = strtok (criteria," "))) {
  819.                 /* for each criterion */
  820.     for (; criteria; (criteria = strtok (NIL," "))) {
  821.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  822.       switch (*ucase (criteria)) {
  823.       case 'A':            /* possible ALL, ANSWERED */
  824.     if (!strcmp (criteria+1,"LL")) f = bezerk_search_all;
  825.     else if (!strcmp (criteria+1,"NSWERED")) f = bezerk_search_answered;
  826.     break;
  827.       case 'B':            /* possible BCC, BEFORE, BODY */
  828.     if (!strcmp (criteria+1,"CC"))
  829.       f = bezerk_search_string (bezerk_search_bcc,&d,&n);
  830.     else if (!strcmp (criteria+1,"EFORE"))
  831.       f = bezerk_search_date (bezerk_search_before,&n);
  832.     else if (!strcmp (criteria+1,"ODY"))
  833.       f = bezerk_search_string (bezerk_search_body,&d,&n);
  834.     break;
  835.       case 'C':            /* possible CC */
  836.     if (!strcmp (criteria+1,"C"))
  837.       f = bezerk_search_string (bezerk_search_cc,&d,&n);
  838.     break;
  839.       case 'D':            /* possible DELETED */
  840.     if (!strcmp (criteria+1,"ELETED")) f = bezerk_search_deleted;
  841.     break;
  842.       case 'F':            /* possible FLAGGED, FROM */
  843.     if (!strcmp (criteria+1,"LAGGED")) f = bezerk_search_flagged;
  844.     else if (!strcmp (criteria+1,"ROM"))
  845.       f = bezerk_search_string (bezerk_search_from,&d,&n);
  846.     break;
  847.       case 'K':            /* possible KEYWORD */
  848.     if (!strcmp (criteria+1,"EYWORD"))
  849.       f = bezerk_search_flag (bezerk_search_keyword,&d);
  850.     break;
  851.       case 'N':            /* possible NEW */
  852.     if (!strcmp (criteria+1,"EW")) f = bezerk_search_new;
  853.     break;
  854.  
  855.       case 'O':            /* possible OLD, ON */
  856.     if (!strcmp (criteria+1,"LD")) f = bezerk_search_old;
  857.     else if (!strcmp (criteria+1,"N"))
  858.       f = bezerk_search_date (bezerk_search_on,&n);
  859.     break;
  860.       case 'R':            /* possible RECENT */
  861.     if (!strcmp (criteria+1,"ECENT")) f = bezerk_search_recent;
  862.     break;
  863.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  864.     if (!strcmp (criteria+1,"EEN")) f = bezerk_search_seen;
  865.     else if (!strcmp (criteria+1,"INCE"))
  866.       f = bezerk_search_date (bezerk_search_since,&n);
  867.     else if (!strcmp (criteria+1,"UBJECT"))
  868.       f = bezerk_search_string (bezerk_search_subject,&d,&n);
  869.     break;
  870.       case 'T':            /* possible TEXT, TO */
  871.     if (!strcmp (criteria+1,"EXT"))
  872.       f = bezerk_search_string (bezerk_search_text,&d,&n);
  873.     else if (!strcmp (criteria+1,"O"))
  874.       f = bezerk_search_string (bezerk_search_to,&d,&n);
  875.     break;
  876.       case 'U':            /* possible UN* */
  877.     if (criteria[1] == 'N') {
  878.       if (!strcmp (criteria+2,"ANSWERED")) f = bezerk_search_unanswered;
  879.       else if (!strcmp (criteria+2,"DELETED")) f = bezerk_search_undeleted;
  880.       else if (!strcmp (criteria+2,"FLAGGED")) f = bezerk_search_unflagged;
  881.       else if (!strcmp (criteria+2,"KEYWORD"))
  882.         f = bezerk_search_flag (bezerk_search_unkeyword,&d);
  883.       else if (!strcmp (criteria+2,"SEEN")) f = bezerk_search_unseen;
  884.     }
  885.     break;
  886.       default:            /* we will barf below */
  887.     break;
  888.       }
  889.       if (!f) {            /* if can't determine any criteria */
  890.     sprintf (LOCAL->buf,"Unknown search criterion: %.30s",criteria);
  891.     mm_log (LOCAL->buf,ERROR);
  892.     return;
  893.       }
  894.                 /* run the search criterion */
  895.       for (i = 1; i <= stream->nmsgs; ++i)
  896.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  897.       mail_elt (stream,i)->searched = NIL;
  898.     }
  899.                 /* report search results to main program */
  900.     for (i = 1; i <= stream->nmsgs; ++i)
  901.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  902.   }
  903. }
  904.  
  905. /* Berkeley mail ping mailbox
  906.  * Accepts: MAIL stream
  907.  * Returns: T if stream alive, else NIL
  908.  * No-op for readonly files, since read/writer can expunge it from under us!
  909.  */
  910.  
  911. long bezerk_ping (stream)
  912.     MAILSTREAM *stream;
  913. {
  914.   char lock[MAILTMPLEN];
  915.   struct stat sbuf;
  916.   int fd;
  917.                 /* does he want to give up readwrite? */
  918.   if (stream->readonly && LOCAL->ld) {
  919.     flock (LOCAL->ld,LOCK_UN);    /* yes, release the lock */
  920.     close (LOCAL->ld);        /* close the lock file */
  921.     LOCAL->ld = NIL;        /* no more lock fd */
  922.     unlink (LOCAL->lname);    /* delete it */
  923.     fs_give ((void **) &LOCAL->lname);
  924.   }
  925.                 /* make sure it is alright to do this at all */
  926.   if (LOCAL && LOCAL->ld && !stream->lock) {
  927.                 /* get current mailbox size */
  928.     stat (LOCAL->name,&sbuf);    /* parse if mailbox changed */
  929.     if ((sbuf.st_size != LOCAL->filesize) &&
  930.     ((fd = bezerk_parse (stream,lock,LOCK_SH)) >= 0)) {
  931.                 /* unlock mailbox */
  932.       bezerk_unlock (fd,stream,lock);
  933.       mail_unlock (stream);    /* and stream */
  934.     }
  935.   }
  936.   return LOCAL ? T : NIL;    /* return if still alive */
  937. }
  938.  
  939. /* Berkeley mail check mailbox
  940.  * Accepts: MAIL stream
  941.  * No-op for readonly files, since read/writer can expunge it from under us!
  942.  */
  943.  
  944. void bezerk_check (stream)
  945.     MAILSTREAM *stream;
  946. {
  947.   char lock[MAILTMPLEN];
  948.   int fd;
  949.                 /* parse and lock mailbox */
  950.   if (LOCAL && LOCAL->ld && ((fd = bezerk_parse (stream,lock,LOCK_EX)) >= 0)) {
  951.     if (LOCAL->dirty && bezerk_extend (stream,fd,"Unable to update mailbox"))
  952.       bezerk_save (stream,fd);    /* dump checkpoint if needed */
  953.                 /* flush locks */
  954.     bezerk_unlock (fd,stream,lock);
  955.     mail_unlock (stream);
  956.   }
  957.   if (LOCAL && LOCAL->ld) mm_log ("Check completed",NIL);
  958. }
  959.  
  960. /* Berkeley mail expunge mailbox
  961.  * Accepts: MAIL stream
  962.  */
  963.  
  964. void bezerk_expunge (stream)
  965.     MAILSTREAM *stream;
  966. {
  967.   int fd,j;
  968.   long i = 1;
  969.   long n = 0;
  970.   unsigned long recent;
  971.   MESSAGECACHE *elt;
  972.   char *r = "No messages deleted, so no update needed";
  973.   char lock[MAILTMPLEN];
  974.   if (LOCAL && LOCAL->ld) {    /* parse and lock mailbox */
  975.     if ((fd = bezerk_parse (stream,lock,LOCK_EX)) >= 0) {
  976.       recent = stream->recent;    /* get recent now that new ones parsed */
  977.       while ((j = (i<=stream->nmsgs)) && !(elt = mail_elt (stream,i))->deleted)
  978.     i++;            /* find first deleted message */
  979.       if (j) {            /* found one? */
  980.                 /* make sure we can do the worst case thing */
  981.     if (bezerk_extend (stream,fd,"Unable to expunge mailbox")) {
  982.       do {            /* flush deleted messages */
  983.         if ((elt = mail_elt (stream,i))->deleted) {
  984.                 /* if recent, note one less recent message */
  985.           if (elt->recent) --recent;
  986.                 /* flush local cache entry */
  987.           fs_give ((void **) &LOCAL->msgs[i - 1]);
  988.           for (j = i; j < stream->nmsgs; j++)
  989.         LOCAL->msgs[j - 1] = LOCAL->msgs[j];
  990.           LOCAL->msgs[stream->nmsgs - 1] = NIL;
  991.                 /* notify upper levels */
  992.           mail_expunged (stream,i);
  993.           n++;        /* count another expunged message */
  994.         }
  995.         else i++;        /* otherwise try next message */
  996.       } while (i <= stream->nmsgs);
  997.                 /* dump checkpoint of the results */
  998.       bezerk_save (stream,fd);
  999.       sprintf ((r = LOCAL->buf),"Expunged %d messages",n);
  1000.     }
  1001.       }
  1002.                 /* notify upper level, free locks */
  1003.       mail_exists (stream,stream->nmsgs);
  1004.       mail_recent (stream,recent);
  1005.       bezerk_unlock (fd,stream,lock);
  1006.       mail_unlock (stream);
  1007.     }
  1008.   }
  1009.   else r = "Expunge ignored on readonly mailbox";
  1010.   if (LOCAL && !stream->silent) mm_log (r,NIL);
  1011. }
  1012.  
  1013. /* Berkeley mail copy message(s)
  1014.     s;
  1015.  * Accepts: MAIL stream
  1016.  *        sequence
  1017.  *        destination mailbox
  1018.  * Returns: T if copy successful, else NIL
  1019.  */
  1020.  
  1021. long bezerk_copy (stream,sequence,mailbox)
  1022.     MAILSTREAM *stream;
  1023.     char *sequence;
  1024.     char *mailbox;
  1025. {
  1026.                 /* copy the messages */
  1027.   return (mail_sequence (stream,sequence)) ?
  1028.     bezerk_copy_messages (stream,mailbox) : NIL;
  1029. }
  1030.  
  1031.  
  1032. /* Berkeley mail move message(s)
  1033.     s;
  1034.  * Accepts: MAIL stream
  1035.  *        sequence
  1036.  *        destination mailbox
  1037.  * Returns: T if move successful, else NIL
  1038.  */
  1039.  
  1040. long bezerk_move (stream,sequence,mailbox)
  1041.     MAILSTREAM *stream;
  1042.     char *sequence;
  1043.     char *mailbox;
  1044. {
  1045.   long i;
  1046.   MESSAGECACHE *elt;
  1047.   if (!(mail_sequence (stream,sequence) &&
  1048.     bezerk_copy_messages (stream,mailbox))) return NIL;
  1049.                 /* delete all requested messages */
  1050.   for (i = 1; i <= stream->nmsgs; i++)
  1051.     if ((elt = mail_elt (stream,i))->sequence) {
  1052.       elt->deleted = T;        /* mark message deleted */
  1053.                 /* recalculate Status/X-Status lines */
  1054.       bezerk_update_status (LOCAL->msgs[i - 1]->status,elt);
  1055.       LOCAL->dirty = T;        /* note stream is now dirty */
  1056.     }
  1057.   return T;
  1058. }
  1059.  
  1060. /* Berkeley mail append message from stringstruct
  1061.  * Accepts: MAIL stream
  1062.  *        destination mailbox
  1063.  *        stringstruct of messages to append
  1064.  * Returns: T if append successful, else NIL
  1065.  */
  1066.  
  1067. long bezerk_append (stream,mailbox,message)
  1068.     MAILSTREAM *stream;
  1069.     char *mailbox;
  1070.     STRING *message;
  1071. {
  1072.   struct stat sbuf;
  1073.   int fd;
  1074.   char c,tmp[MAILTMPLEN],lock[MAILTMPLEN];
  1075.   int i = 0;
  1076.   char *s = tmp;
  1077.   long t = time (0);
  1078.   long size = SIZE (message);
  1079.   if ((fd = bezerk_lock (bezerk_file (tmp,mailbox),O_WRONLY|O_APPEND|O_CREAT,
  1080.              S_IREAD|S_IWRITE,lock,LOCK_EX)) < 0) {
  1081.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1082.     mm_log (tmp,ERROR);
  1083.     return NIL;
  1084.   }
  1085.   mm_critical (stream);        /* go critical */
  1086.   fstat (fd,&sbuf);        /* get current file size */
  1087.   sprintf (tmp,"From %s@%s %s",myusername (),lhostn,ctime (&t));
  1088.                 /* write header */
  1089.   if (write (fd,tmp,strlen (tmp)) < 0) {
  1090.     sprintf (tmp,"Header write failed: %s",strerror (errno));
  1091.     mm_log (tmp,ERROR);
  1092.     ftruncate (fd,sbuf.st_size);
  1093.   }
  1094.   else while (size--) {        /* copy text, tossing out CR's */
  1095.     if ((c = SNX (message)) != '\015') s[i++] = c;
  1096.                 /* dump if full buffer, no more data, or NL */
  1097.     if ((!size) || (i == MAILTMPLEN) || (c == '\n')) {
  1098.                 /* prepend broken to leading "From " */
  1099.       if ((i > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
  1100.       (tmp[3] == 'm') && (tmp[4] == ' ')) write (fd,">",1);
  1101.       if ((write (fd,tmp,i)) < 0) {
  1102.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1103.     mm_log (tmp,ERROR);
  1104.     ftruncate (fd,sbuf.st_size);
  1105.     break;
  1106.       }
  1107.       i = 0;            /* restart buffer */
  1108.                 /* write out final newline if at end */
  1109.       if (!size) write (fd,"\n",1);
  1110.     }
  1111.   }
  1112.   fsync (fd);            /* force out the update */
  1113.   bezerk_unlock (fd,NIL,lock);    /* unlock and close mailbox */
  1114.   mm_nocritical (stream);    /* release critical */
  1115.   return T;            /* return success */
  1116. }
  1117.  
  1118. /* Berkeley garbage collect stream
  1119.  * Accepts: Mail stream
  1120.  *        garbage collection flags
  1121.  */
  1122.  
  1123. void bezerk_gc (stream,gcflags)
  1124.     MAILSTREAM *stream;
  1125.     long gcflags;
  1126. {
  1127.   /* nothing here for now */
  1128. }
  1129.  
  1130. /* Internal routines */
  1131.  
  1132.  
  1133. /* Berkeley mail abort stream
  1134.  * Accepts: MAIL stream
  1135.  */
  1136.  
  1137. void bezerk_abort (stream)
  1138.     MAILSTREAM *stream;
  1139. {
  1140.   long i;
  1141.   if (LOCAL) {            /* only if a file is open */
  1142.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  1143.     if (LOCAL->ld) {        /* have a mailbox lock? */
  1144.       flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
  1145.       close (LOCAL->ld);    /* close the lock file */
  1146.       unlink (LOCAL->lname);    /* and delete it */
  1147.     }
  1148.     fs_give ((void **) &LOCAL->lname);
  1149.     if (LOCAL->msgs) {        /* free local cache */
  1150.       for (i = 0; i < stream->nmsgs; ++i) fs_give ((void **) &LOCAL->msgs[i]);
  1151.       fs_give ((void **) &LOCAL->msgs);
  1152.     }
  1153.                 /* free local text buffers */
  1154.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  1155.                 /* nuke the local data */
  1156.     fs_give ((void **) &stream->local);
  1157.     stream->dtb = NIL;        /* log out the DTB */
  1158.   }
  1159. }
  1160.  
  1161. /* Berkeley mail generate file string
  1162.  * Accepts: temporary buffer to write into
  1163.  *        mailbox name string
  1164.  * Returns: local file string
  1165.  */
  1166.  
  1167.  
  1168. char *bezerk_file (dst,name)
  1169.     char *dst;
  1170.     char *name;
  1171. {
  1172.   struct passwd *pw;
  1173.   char *s,*t,tmp[MAILTMPLEN];
  1174.   switch (*name) {
  1175.   case '*':            /* bboard? */
  1176.     sprintf (tmp,"~ftp/%s",(name[1] == '/') ? name+2 : name+1);
  1177.     dst = bezerk_file (dst,tmp);/* recurse to get result */
  1178.     break;
  1179.   case '/':            /* absolute file path */
  1180.     strcpy (dst,name);        /* copy the mailbox name */
  1181.     break;
  1182.   case '~':            /* home directory */
  1183.     if (name[1] == '/') t = myhomedir ();
  1184.     else {
  1185.       strcpy (tmp,name + 1);    /* copy user name */
  1186.       if (s = strchr (tmp,'/')) *s = '\0';
  1187.       t = ((pw = getpwnam (tmp)) && pw->pw_dir) ? pw->pw_dir : "/NOSUCHUSER";
  1188.     }
  1189.     sprintf (dst,"%s%s",t,(s = strchr (name,'/')) ? s : "");
  1190.     break;
  1191.   default:            /* other name */
  1192.     if (strcmp (ucase (strcpy (dst,name)),"INBOX"))
  1193.       sprintf (dst,"%s/%s",myhomedir (),name);
  1194.                 /* INBOX becomes mail spool directory file */
  1195.     else sprintf (dst,MAILFILE,myusername ());
  1196.   }
  1197.   return dst;
  1198. }
  1199.  
  1200. /* Berkeley open and lock mailbox
  1201.  * Accepts: file name to open/lock
  1202.  *        file open mode
  1203.  *        destination buffer for lock file name
  1204.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1205.  */
  1206.  
  1207. int bezerk_lock (file,flags,mode,lock,op)
  1208.     char *file;
  1209.     int flags;
  1210.     int mode;
  1211.     char *lock;
  1212.     int op;
  1213. {
  1214.   int fd,ld,j;
  1215.   int i = LOCKTIMEOUT * 60 - 1;
  1216.   char tmp[MAILTMPLEN];
  1217.   struct timeval tp;
  1218.   struct stat sb;
  1219.                 /* build lock filename */
  1220.   strcat (bezerk_file (lock,file),".lock");
  1221.   do {                /* until OK or out of tries */
  1222.     gettimeofday (&tp,NIL);    /* get the time now */
  1223. #ifdef NFSKLUDGE
  1224.   /* SUN-OS had an NFS, As kludgy as an albatross;
  1225.    * And everywhere that it was installed, It was a total loss.  -- MRC 9/25/91
  1226.    */
  1227.                 /* build hitching post file name */
  1228.     sprintf (tmp,"%s.%d.%d.",lock,time (0),getpid ());
  1229.     j = strlen (tmp);        /* append local host name */
  1230.     gethostname (tmp + j,(MAILTMPLEN - j) - 1);
  1231.                 /* try to get hitching-post file */
  1232.     if ((ld = open (tmp,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) {
  1233.                 /* prot fail & non-ex, don't use lock files */
  1234.       if ((errno == EACCES) && (stat (tmp,&sb))) *lock = '\0';
  1235.       else {            /* otherwise something strange is going on */
  1236.     sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  1237.     mm_log (tmp,WARN);    /* this is probably not good */
  1238.                 /* don't use lock files if not one of these */
  1239.     if ((errno != EEXIST) && (errno != EACCES)) *lock = '\0';
  1240.       }
  1241.     }
  1242.     else {            /* got a hitching-post */
  1243.       chmod (tmp,0666);        /* make sure others can break the lock */
  1244.       close (ld);        /* close the hitching-post */
  1245.       link (tmp,lock);        /* tie hitching-post to lock, ignore failure */
  1246.       stat (tmp,&sb);        /* get its data */
  1247.       unlink (tmp);        /* flush hitching post */
  1248.       /* If link count .ne. 2, hitch failed.  Set ld to -1 as if open() failed
  1249.      so we try again.  If extant lock file and time now is .gt. file time
  1250.      plus timeout interval, flush the lock so can win next time around. */
  1251.       if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
  1252.       (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)) unlink (lock);
  1253.     }
  1254.  
  1255. #else
  1256.   /* This works on modern Unix systems which are not afflicted with NFS mail.
  1257.    * "Modern" means that O_EXCL works.  I think that NFS mail is a terrible
  1258.    * idea -- that's what IMAP is for -- but some people insist upon losing...
  1259.    */
  1260.                 /* try to get the lock */
  1261.     if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) switch (errno) {
  1262.     case EEXIST:        /* if extant and old, grab it for ourselves */
  1263.       if ((!stat (lock,&sb)) && tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)
  1264.     ld = open (lock,O_WRONLY|O_CREAT,0666);
  1265.       break;
  1266.     case EACCES:        /* protection fail, ignore if non-ex or old */
  1267.       if (stat (lock,&sb) || (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60))
  1268.     *lock = '\0';        /* assume no world write mail spool dir */
  1269.       break;
  1270.     default:            /* some other failure */
  1271.       sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
  1272.       mm_log (tmp,WARN);    /* this is probably not good */
  1273.       *lock = '\0';        /* don't use lock files */
  1274.       break;
  1275.     }
  1276.     if (ld >= 0) {        /* if made a lock file */
  1277.       chmod (tmp,0666);        /* make sure others can break the lock */
  1278.       close (ld);        /* close the lock file */
  1279.     }
  1280. #endif
  1281.     if ((ld < 0) && *lock) {    /* if failed to make lock file and retry OK */
  1282.       if (!(i%15)) {
  1283.     sprintf (tmp,"Mailbox %s is locked, will override in %d seconds...",
  1284.          file,i);
  1285.     mm_log (tmp,WARN);
  1286.       }
  1287.       sleep (1);        /* wait 1 second before next try */
  1288.     }
  1289.   } while (i-- && ld < 0 && *lock);
  1290.                 /* open file */
  1291.   if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
  1292.   else {            /* open failed */
  1293.     j = errno;            /* preserve error code */
  1294.     if (*lock) unlink (lock);    /* flush the lock file if any */
  1295.     errno = j;            /* restore error code */
  1296.   }
  1297.   return fd;
  1298. }
  1299.  
  1300. /* Berkeley unlock and close mailbox
  1301.  * Accepts: file descriptor
  1302.  *        (optional) mailbox stream to check atime/mtime
  1303.  *        (optional) lock file name
  1304.  */
  1305.  
  1306. void bezerk_unlock (fd,stream,lock)
  1307.     int fd;
  1308.     MAILSTREAM *stream;
  1309.     char *lock;
  1310. {
  1311.   struct stat sbuf;
  1312.   struct timeval tp[2];
  1313.   fstat (fd,&sbuf);        /* get file times */
  1314.                 /* if stream and csh would think new mail */
  1315.   if (stream && (sbuf.st_atime <= sbuf.st_mtime)) {
  1316.     gettimeofday (&tp[0],NIL);    /* set atime to now */
  1317.                 /* set mtime to (now - 1) if necessary */
  1318.     tp[1].tv_sec = tp[0].tv_sec > sbuf.st_mtime ? sbuf.st_mtime :
  1319.       tp[0].tv_sec - 1;
  1320.     tp[1].tv_usec = 0;        /* oh well */
  1321.                 /* set the times, note change */
  1322.     if (!utimes (LOCAL->name,tp)) LOCAL->filetime = tp[1].tv_sec;
  1323.   }
  1324.   flock (fd,LOCK_UN);        /* release flock'ers */
  1325.   close (fd);            /* close the file */
  1326.                 /* flush the lock file if any */
  1327.   if (lock && *lock) unlink (lock);
  1328. }
  1329.  
  1330. /* Berkeley mail parse and lock mailbox
  1331.  * Accepts: MAIL stream
  1332.  *        space to write lock file name
  1333.  *        type of locking operation
  1334.  * Returns: file descriptor if parse OK, mailbox is locked shared
  1335.  *        -1 if failure, stream aborted
  1336.  */
  1337.  
  1338. int bezerk_parse (stream,lock,op)
  1339.     MAILSTREAM *stream;
  1340.     char *lock;
  1341.     int op;
  1342. {
  1343.   int fd;
  1344.   long delta,i,j,is,is1;
  1345.   char *s,*s1,*t = NIL,*e;
  1346.   int ti = 0,zn = 0;
  1347.   int first = T;
  1348.   long nmsgs = stream->nmsgs;
  1349.   long recent = stream->recent;
  1350.   struct stat sbuf;
  1351.   MESSAGECACHE *elt;
  1352.   FILECACHE *m = NIL,*n = NIL;
  1353.   mail_lock (stream);        /* guard against recursion or pingers */
  1354.                 /* open and lock mailbox (shared OK) */
  1355.   if ((fd = bezerk_lock (LOCAL->name,LOCAL->ld ? O_RDWR : O_RDONLY,NIL,
  1356.              lock,op)) < 0) {
  1357.                 /* failed, OK for non-ex file? */
  1358.     if ((errno != ENOENT) || LOCAL->filesize) {
  1359.       sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
  1360.       mm_log (LOCAL->buf,ERROR);
  1361.       bezerk_abort (stream);
  1362.     }
  1363.     else {            /* this is to allow for non-ex INBOX */
  1364.       mail_exists (stream,0);    /* make sure upper level sees this as empty */
  1365.       mail_recent (stream,0);
  1366.     }
  1367.     mail_unlock (stream);
  1368.     return -1;
  1369.   }
  1370.   fstat (fd,&sbuf);        /* get status */
  1371.                 /* calculate change in size */
  1372.   if ((delta = sbuf.st_size - LOCAL->filesize) < 0) {
  1373.     sprintf (LOCAL->buf,"Mailbox shrank from %d to %d bytes, aborted",
  1374.          LOCAL->filesize,sbuf.st_size);
  1375.     mm_log (LOCAL->buf,ERROR);    /* this is pretty bad */
  1376.     bezerk_unlock (fd,stream,lock);
  1377.     bezerk_abort (stream);
  1378.     mail_unlock (stream);
  1379.     return -1;
  1380.   }
  1381.  
  1382.   else if (delta) {        /* get to that position in the file */
  1383.     lseek (fd,LOCAL->filesize,L_SET);
  1384.     s = s1 = LOCAL->buf;    /* initial read-in location */
  1385.     i = 0;            /* initial unparsed read-in count */
  1386.     do {
  1387.       i = min (CHUNK,delta);    /* calculate read-in size */
  1388.                 /* increase the read-in buffer if necessary */
  1389.       if ((j = i + (s1 - s)) >= LOCAL->buflen) {
  1390.     is = s - LOCAL->buf;    /* note former start of message position */
  1391.     is1 = s1 - LOCAL->buf;    /* and start of new data position */
  1392.     if (s1 - s) fs_resize ((void **) &LOCAL->buf,(LOCAL->buflen = j) + 1);
  1393.     else {            /* fs_resize would do an unnecessary copy */
  1394.       fs_give ((void **) &LOCAL->buf);
  1395.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
  1396.     }
  1397.     s = LOCAL->buf + is;    /* new start of message */
  1398.     s1 = LOCAL->buf + is1;    /* new start of new data */
  1399.       }
  1400.       s1[i] = '\0';        /* tie off chunk */
  1401.       if (read (fd,s1,i) < 0) {    /* read a chunk of new text */
  1402.     sprintf (LOCAL->buf,"Error reading mail file: %s",strerror (errno));
  1403.     mm_log (LOCAL->buf,ERROR);
  1404.     bezerk_unlock (fd,stream,lock);
  1405.     bezerk_abort (stream);
  1406.     mail_unlock (stream);
  1407.     return -1;
  1408.       }
  1409.       delta -= i;        /* account for data read in */
  1410.                 /* validate newly-appended data */
  1411.       if (first) {        /* only do this first time! */
  1412.     if (!((*s =='F') && VALID (s,t,ti,zn))) {
  1413.       mm_log ("Mailbox format invalidated (consult an expert), aborted",
  1414.           ERROR);
  1415.       bezerk_unlock (fd,stream,lock);
  1416.       bezerk_abort (stream);
  1417.       mail_unlock (stream);
  1418.       return -1;
  1419.     }
  1420.     first = NIL;        /* don't do this again */
  1421.       }
  1422.  
  1423.                 /* found end of message or end of data? */
  1424.       while ((e = bezerk_eom (s,s1,i)) || !delta) {
  1425.     nmsgs++;        /* yes, have a new message */
  1426.     if (e) j = (e - s) - 1;    /* calculate message length */
  1427.     else j = strlen (s) - 1;/* otherwise is remainder of data */
  1428.     if (m) {        /* new cache needed, have previous data? */
  1429.       n->header = (char *) fs_get (sizeof (FILECACHE) + j + 1);
  1430.       n = (FILECACHE *) n->header;
  1431.     }
  1432.     else m = n = (FILECACHE *) fs_get (sizeof (FILECACHE) + j + 1);
  1433.                 /* copy message data */
  1434.     strncpy (n->internal,s,j);
  1435.     n->internal[j] = '\0';
  1436.     n->header = NIL;    /* initially no link */
  1437.     n->headersize = j;    /* stash away buffer length */
  1438.     if (e) {        /* saw end of message? */
  1439.       i -= e - s1;        /* new unparsed data count */
  1440.       s = s1 = e;        /* advance to new message */
  1441.     }
  1442.     else break;        /* else punt this loop */
  1443.       }
  1444.       if (delta) {        /* end of message not found? */
  1445.     s1 += i;        /* end of unparsed data */
  1446.     if (s != LOCAL->buf){    /* message doesn't begin at buffer? */
  1447.       i = s1 - s;        /* length of message so far */
  1448.       memmove (LOCAL->buf,s,i);
  1449.       s = LOCAL->buf;    /* message now starts at buffer origin */
  1450.       s1 = s + i;        /* calculate new end of unparsed data */
  1451.     }
  1452.       }
  1453.     } while (delta);        /* until nothing more new to read */
  1454.   }
  1455.   else {            /* no change, don't babble if never got time */
  1456.     if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
  1457.       mm_log ("New mailbox modification time but apparently no changes",WARN);
  1458.   }
  1459.                 /* expand the primary cache */
  1460.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1461.   if (LOCAL->msgs) {        /* have a cache yet? */
  1462.     if (nmsgs >= LOCAL->cachesize)
  1463.       fs_resize ((void **) &LOCAL->msgs,
  1464.          (LOCAL->cachesize += CACHEINCREMENT) * sizeof (FILECACHE *));
  1465.   }
  1466.   else LOCAL->msgs = (FILECACHE **)
  1467.     fs_get ((LOCAL->cachesize = nmsgs + CACHEINCREMENT) *sizeof (FILECACHE *));
  1468.  
  1469.   if (LOCAL->buflen > CHUNK) {    /* maybe move where the buffer is in memory*/
  1470.     fs_give ((void **) &LOCAL->buf);
  1471.     LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNK) + 1);
  1472.   }
  1473.   for (i = stream->nmsgs, n = m; i < nmsgs; i++) {
  1474.     LOCAL->msgs[i] = m = n;    /* set cache, and next cache pointer */
  1475.     n = (FILECACHE *) n->header;
  1476.     /* This is a bugtrap for bogons in the new message cache, which may happen
  1477.      * if memory is corrupted.  Note that in the case of a totally empty
  1478.      * message, a newline is appended and counts adjusted.
  1479.      */
  1480.     if (!((s = m->internal) && VALID (s,t,ti,zn)))
  1481.       if (!(s && !strchr (s,'\n') && strcat (s,"\n") && VALID (s,t,ti,zn) &&
  1482.         m->headersize++))
  1483.     fatal ("Bogus entry in new cache list");
  1484.                 /* pointer to message header */
  1485.     m->header = s = strchr (t++,'\n') + 1;
  1486.     m->headersize -= m->header - m->internal;
  1487.     m->body = NIL;        /* assume no body as yet */
  1488.     m->bodysize = 0;
  1489.     recent++;            /* assume recent by default */
  1490.     (elt = mail_elt (stream,i+1))->recent = T;
  1491.                 /* calculate initial Status/X-Status lines */
  1492.     bezerk_update_status (m->status,elt);
  1493.                 /* generate plausable IMAPish date string */
  1494.     LOCAL->buf[2] = LOCAL->buf[6] = LOCAL->buf[20] = '-';
  1495.     LOCAL->buf[11] = ' ';
  1496.     LOCAL->buf[14] = LOCAL->buf[17] = ':';
  1497.                 /* dd */
  1498.     LOCAL->buf[0] = t[ti - 3]; LOCAL->buf[1] = t[ti - 2];
  1499.                 /* mmm */
  1500.     LOCAL->buf[3] = t[ti - 7]; LOCAL->buf[4] = t[ti - 6];
  1501.     LOCAL->buf[5] = t[ti - 5];
  1502.                 /* hh */
  1503.     LOCAL->buf[12] = t[ti]; LOCAL->buf[13] = t[ti + 1];
  1504.                 /* mm */
  1505.     LOCAL->buf[15] = t[ti + 3]; LOCAL->buf[16] = t[ti + 4];
  1506.     if (t[ti += 5] == ':') {    /* ss if present */
  1507.       LOCAL->buf[18] = t[++ti];
  1508.       LOCAL->buf[19] = t[++ti];
  1509.       ti++;            /* move to space */
  1510.     }
  1511.     else LOCAL->buf[18] = LOCAL->buf[19] = '0';
  1512.                 /* yy -- advance over timezone if necessary */
  1513.     if (zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
  1514.     LOCAL->buf[7] = t[ti]; LOCAL->buf[8] = t[ti + 1];
  1515.     LOCAL->buf[9] = t[ti + 2]; LOCAL->buf[10] = t[ti + 3];
  1516.                 /* zzz */
  1517.     t = zn ? (t + zn) : "LCL";
  1518.     LOCAL->buf[21] = *t++; LOCAL->buf[22] = *t++; LOCAL->buf[23] = *t++;
  1519.     if ((LOCAL->buf[21] != '+') && (LOCAL->buf[21] != '-'))
  1520.       LOCAL->buf[24] = '\0';
  1521.     else {            /* numeric time zone */
  1522.       LOCAL->buf[24] = *t++; LOCAL->buf[25] = *t++;
  1523.       LOCAL->buf[26] = '\0'; LOCAL->buf[20] = ' ';
  1524.     }
  1525.                 /* set internal date */
  1526.     if (!mail_parse_date (elt,LOCAL->buf)) mm_log ("Unparsable date",WARN);
  1527.  
  1528.     is = 0;            /* initialize newline count */
  1529.     e = NIL;            /* no status stuff yet */
  1530.     do switch (*(t = s)) {    /* look at header lines */
  1531.     case 'X':            /* possible X-Status: line */
  1532.       if (s[1] == '-' && s[2] == 'S') s += 2;
  1533.       else {
  1534.     is++;            /* count another newline */
  1535.     break;            /* this is uninteresting after all */
  1536.       }
  1537.     case 'S':            /* possible Status: line */
  1538.       if (s[1] == 't' && s[2] == 'a' && s[3] == 't' && s[4] == 'u' &&
  1539.       s[5] == 's' && s[6] == ':') {
  1540.     if (!e) e = t;        /* note deletion point */
  1541.     s += 6;            /* advance to status flags */
  1542.     do switch (*s++) {    /* parse flags */
  1543.     case 'R':        /* message read */
  1544.       elt->seen = T;
  1545.       break;
  1546.     case 'O':        /* message old */
  1547.       if (elt->recent) {    /* don't do this more than once! */
  1548.         elt->recent = NIL;
  1549.         recent--;        /* not recent any longer... */
  1550.       }
  1551.       break;
  1552.     case 'D':        /* message deleted */
  1553.       elt->deleted = T;
  1554.       break;
  1555.     case 'F':        /* message flagged */
  1556.       elt ->flagged = T;
  1557.       break;
  1558.     case 'A':        /* message answered */
  1559.       elt ->answered = T;
  1560.       break;
  1561.     default:        /* some other crap */
  1562.       break;
  1563.     } while (*s && *s != '\n');
  1564.                 /* recalculate Status/X-Status lines */
  1565.     bezerk_update_status (m->status,elt);
  1566.       }
  1567.       else is++;        /* otherwise random line */
  1568.       break;
  1569.  
  1570.     case '\n':            /* end of header */
  1571.       m->body = ++s;        /* start of body is here */
  1572.       j = m->body - m->header;    /* new header size */
  1573.                 /* calculate body size */
  1574.       m->bodysize = m->headersize - j;
  1575.       if (e) {            /* saw status poop? */
  1576.     *e++ = '\n';        /* patch in trailing newline */
  1577.     m->headersize = e - m->header;
  1578.       }
  1579.       else m->headersize = j;    /* set header size */
  1580.       s = NIL;            /* don't scan any further */
  1581.       is++;            /* count a newline */
  1582.       break;
  1583.     case '\0':            /* end of message */
  1584.       if (e) {            /* saw status poop? */
  1585.     *e++ = '\n';        /* patch in trailing newline */
  1586.     m->headersize = e - m->header;
  1587.       }
  1588.       is++;            /* count an extra newline here */
  1589.       break;
  1590.     default:            /* anything else is uninteresting */
  1591.       if (e) {            /* have status stuff to worry about? */
  1592.     j = s - e;        /* yuck!!  calculate size of delete area */
  1593.                 /* blat remaining number of bytes down */
  1594.     memmove (e,s,m->header + m->headersize - s);
  1595.     m->headersize -= j;    /* update for new size */
  1596.     e = NIL;        /* no more delete area */
  1597.                 /* tie off old cruft */
  1598.     *(m->header + m->headersize) = '\0';
  1599.       }
  1600.       is++;
  1601.       break;
  1602.     } while (s && (s = strchr (s,'\n')) && s++);
  1603.                 /* count newlines in body */
  1604.     if (s = m->body) while (*s) if (*s++ == '\n') is++;
  1605.     elt->rfc822_size = m->headersize + m->bodysize + is;
  1606.   }
  1607.   if (n) fatal ("Cache link-list inconsistency");
  1608.   while (i < LOCAL->cachesize) LOCAL->msgs[i++] = NIL;
  1609.                 /* update parsed file size and time */
  1610.   LOCAL->filesize = sbuf.st_size;
  1611.   LOCAL->filetime = sbuf.st_mtime;
  1612.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1613.   mail_recent (stream,recent);    /* and of change in recent messages */
  1614.   if (recent) LOCAL->dirty = T;    /* mark dirty so O flags are set */
  1615.   return fd;            /* return the winnage */
  1616. }
  1617.  
  1618. /* Berkeley search for end of message
  1619.  * Accepts: start of message
  1620.  *        start of new data
  1621.  *        size of new data
  1622.  * Returns: pointer to start of new message if one found
  1623.  */
  1624.  
  1625. #define Word unsigned long
  1626.  
  1627. char *bezerk_eom (som,sod,i)
  1628.     char *som;
  1629.     char *sod;
  1630.     long i;
  1631. {
  1632.   char *s = sod;        /* find start of line in current message */
  1633.   char *t;
  1634.   int ti,zn;
  1635.   union {
  1636.     unsigned long wd;
  1637.     char ch[9];
  1638.   } wdtest;
  1639.   strcpy (wdtest.ch,"AAAA1234");/* constant for word testing */
  1640.   while ((s > som) && *s-- != '\n');
  1641.   if (wdtest.wd != 0x41414141) {/* not a 32-bit word machine? */
  1642.     while (s = strstr (s,"\nFrom ")) if (s++ && VALID (s,t,ti,zn)) return s;
  1643.   }
  1644.   else {            /* can do it faster this way */
  1645.     register Word m = 0x0a0a0a0a;
  1646.     while ((long) s & 3)    /* any characters before word boundary? */
  1647.       if ((*s++ == '\n') && s[0] == 'F' && VALID (s,t,ti,zn)) return s;
  1648.     i = (sod + i) - s;        /* total number of tries */
  1649.     do {            /* fast search for newline */
  1650.       if (0x80808080 & (0x01010101 + (0x7f7f7f7f & ~(m ^ *(Word *) s)))) {
  1651.                 /* interesting word, check it closer */
  1652.     if (*s++ == '\n' && s[0] == 'F' && VALID (s,t,ti,zn)) return s;
  1653.     else if (*s++ == '\n' && s[0] == 'F' && VALID (s,t,ti,zn)) return s;
  1654.     else if (*s++ == '\n' && s[0] == 'F' && VALID (s,t,ti,zn)) return s;
  1655.     else if (*s++ == '\n' && s[0] == 'F' && VALID (s,t,ti,zn)) return s;
  1656.       }
  1657.       else s += 4;        /* try next word */
  1658.       i -= 4;            /* count a word checked */
  1659.     } while (i > 24);        /* continue until end of plausible string */
  1660.   }
  1661.   return NIL;
  1662. }
  1663.  
  1664. /* Berkeley extend mailbox to reserve worst-case space for expansion
  1665.  * Accepts: MAIL stream
  1666.  *        file descriptor
  1667.  *        error string
  1668.  * Returns: T if extend OK and have gone critical, NIL if should abort
  1669.  */
  1670.  
  1671. int bezerk_extend (stream,fd,error)
  1672.     MAILSTREAM *stream;
  1673.     int fd;
  1674.     char *error;
  1675. {
  1676.   struct stat sbuf;
  1677.   FILECACHE *m;
  1678.   char tmp[MAILTMPLEN];
  1679.   int i,ok;
  1680.   long f;
  1681.   char *s;
  1682.   int retry;
  1683.                 /* calculate estimated size of mailbox */
  1684.   for (i = 0,f = 0; i < stream->nmsgs; i++) {
  1685.     m = LOCAL->msgs[i];        /* get cache pointer */
  1686.     f += (m->header - m->internal) + m->headersize + sizeof (STATUS) +
  1687.       m->bodysize + 1;        /* update guesstimate */
  1688.     }
  1689.   mm_critical (stream);        /* go critical */
  1690.                 /* return now if file large enough */
  1691.   if (f <= LOCAL->filesize) return T;
  1692.   s = (char *) fs_get (f -= LOCAL->filesize);
  1693.   memset (s,0,f);        /* get a block of nulls */
  1694.                 /* get to end of file */
  1695.   lseek (fd,LOCAL->filesize,L_SET);
  1696.   do {
  1697.     retry = NIL;        /* no retry yet */
  1698.     if (!(ok = (write (fd,s,f) >= 0))) {
  1699.       i = errno;        /* note error before doing ftruncate */
  1700.                 /* restore prior file size */
  1701.       ftruncate (fd,LOCAL->filesize);
  1702.       fsync (fd);        /* is this necessary? */
  1703.       fstat (fd,&sbuf);        /* now get updated file time */
  1704.       LOCAL->filetime = sbuf.st_mtime;
  1705.                 /* punt if that's what main program wants */
  1706.       if (mm_diskerror (stream,i,NIL)) {
  1707.     mm_nocritical (stream);    /* exit critical */
  1708.     sprintf (tmp,"%s: %s",error,strerror (i));
  1709.     mm_notify (stream,tmp,WARN);
  1710.       }
  1711.       else retry = T;        /* set to retry */
  1712.     }
  1713.   } while (retry);        /* repeat if need to try again */
  1714.   fs_give ((void **) &s);    /* flush buffer of nulls */
  1715.   return ok;            /* return status */
  1716. }
  1717.  
  1718. /* Berkeley save mailbox
  1719.  * Accepts: MAIL stream
  1720.  *        mailbox file descriptor
  1721.  *
  1722.  * Mailbox must be readwrite and locked for exclusive access.
  1723.  */
  1724.  
  1725. void bezerk_save (stream,fd)
  1726.     MAILSTREAM *stream;
  1727.     int fd;
  1728. {
  1729.   struct stat sbuf;
  1730.   struct iovec iov[16];
  1731.   int iovc;
  1732.   long i;
  1733.   int e;
  1734.   int retry;
  1735.   do {                /* restart point if failure */
  1736.     retry = NIL;        /* no need to retry yet */
  1737.                 /* start at beginning of file */
  1738.     lseek (fd,LOCAL->filesize = 0,L_SET);
  1739.                 /* loop through all messages */
  1740.     for (i = 1,iovc = 0; i <= stream->nmsgs; i++) {
  1741.                 /* set up iov's for this message */
  1742.       bezerk_write_message (iov,&iovc,LOCAL->msgs[i-1]);
  1743.                 /* filled up iovec or end of messages? */
  1744.       if ((iovc == 16) || (i == stream->nmsgs)) {
  1745.                 /* write messages */
  1746.     if ((e = writev (fd,iov,iovc)) < 0) {
  1747.       sprintf (LOCAL->buf,"Unable to rewrite mailbox: %s",
  1748.            strerror (e = errno));
  1749.       mm_log (LOCAL->buf,WARN);
  1750.       mm_diskerror (stream,e,T);
  1751.       retry = T;        /* must retry */
  1752.       break;        /* abort this particular try */
  1753.     }
  1754.     else {            /* won */
  1755.       iovc = 0;        /* restart iovec */
  1756.       LOCAL->filesize += e;    /* count these bytes in data */
  1757.     }
  1758.       }
  1759.     }
  1760.   } while (retry);        /* repeat if need to try again */
  1761.   fsync (fd);            /* make sure the disk has the update */
  1762.                 /* nuke any cruft after that */
  1763.   ftruncate (fd,LOCAL->filesize);
  1764.   fsync (fd);            /* is this necessary? */
  1765.   fstat (fd,&sbuf);        /* now get updated file time */
  1766.   LOCAL->filetime = sbuf.st_mtime;
  1767.   LOCAL->dirty = NIL;        /* stream no longer dirty */
  1768.   mm_nocritical (stream);    /* exit critical */
  1769. }
  1770.  
  1771. /* Berkeley copy messages
  1772.  * Accepts: MAIL stream
  1773.  *        mailbox name
  1774.  * Returns: T if copy successful else NIL
  1775.  */
  1776.  
  1777. int bezerk_copy_messages (stream,mailbox)
  1778.     MAILSTREAM *stream;
  1779.     char *mailbox;
  1780. {
  1781.   char file[MAILTMPLEN];
  1782.   char lock[MAILTMPLEN];
  1783.   struct iovec iov[16];
  1784.   int fd,iovc;
  1785.   struct stat sbuf;
  1786.   long i;
  1787.   int ok = T;
  1788.                 /* make sure valid mailbox */
  1789.   if (!bezerk_isvalid (mailbox,file)) {
  1790.     sprintf (LOCAL->buf,"Not a Berkeley-format mailbox: %s",mailbox);
  1791.     mm_log (LOCAL->buf,ERROR);
  1792.     return NIL;
  1793.   }
  1794.   if ((fd = bezerk_lock (bezerk_file (file,mailbox),O_WRONLY|O_APPEND|O_CREAT,
  1795.              S_IREAD|S_IWRITE,lock,LOCK_EX)) < 0) {
  1796.     sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
  1797.     mm_log (LOCAL->buf,ERROR);
  1798.     return NIL;
  1799.   }
  1800.   mm_critical (stream);        /* go critical */
  1801.   fstat (fd,&sbuf);        /* get current file size */
  1802.                 /* write all requested messages to mailbox */
  1803.   for (i = 1,iovc = 0; ok && i <= stream->nmsgs; i++) {
  1804.                 /* set up iov's if message selected */
  1805.     if (mail_elt (stream,i)->sequence)
  1806.       bezerk_write_message (iov,&iovc,LOCAL->msgs[i - 1]);
  1807.                 /* filled up iovec or end of messages? */
  1808.     if (iovc && ((iovc == 16) || (i == stream->nmsgs))) {
  1809.       if (ok = (writev (fd,iov,iovc) >= 0)) iovc = 0;
  1810.       else {
  1811.     sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
  1812.     mm_log (LOCAL->buf,ERROR);
  1813.     ftruncate (fd,sbuf.st_size);
  1814.     break;
  1815.       }
  1816.     }
  1817.   }
  1818.   fsync (fd);            /* force out the update */
  1819.   bezerk_unlock (fd,NIL,lock);    /* unlock and close mailbox */
  1820.   mm_nocritical (stream);    /* release critical */
  1821.   return ok;            /* return whether or not succeeded */
  1822. }
  1823.  
  1824. /* Berkeley write message to mailbox
  1825.  * Accepts: I/O vector
  1826.  *        I/O vector index
  1827.  *        local cache for this message
  1828.  *
  1829.  * This routine is the reason why the local cache has a copy of the status.
  1830.  * We can be called to dump out the mailbox as part of a stream recycle, since
  1831.  * we don't write out the mailbox when flags change and hence an update may be
  1832.  * needed.  However, at this point the elt has already become history, so we
  1833.  * can't use any information other than what is local to us.
  1834.  */
  1835.  
  1836. void bezerk_write_message (iov,i,m)
  1837.     struct iovec iov[];
  1838.     int *i;
  1839.     FILECACHE *m;
  1840. {
  1841.   iov[*i].iov_base =m->internal;/* pointer/counter to headers */
  1842.                 /* length of internal + message headers */
  1843.   iov[*i].iov_len = (m->header + m->headersize) - m->internal;
  1844.                 /* suppress extra newline if present */
  1845.   if ((iov[*i].iov_base)[iov[*i].iov_len - 2] == '\n') iov[(*i)++].iov_len--;
  1846.   else (*i)++;            /* unlikely but... */
  1847.   iov[*i].iov_base = m->status;    /* pointer/counter to status */
  1848.   iov[(*i)++].iov_len = strlen (m->status);
  1849.   iov[*i].iov_base = m->body;    /* pointer/counter to text body */
  1850.   iov[(*i)++].iov_len = m->bodysize;
  1851.   iov[*i].iov_base = "\n";    /* pointer/counter to extra newline */
  1852.   iov[(*i)++].iov_len = 1;
  1853. }
  1854.  
  1855. /* Berkeley update status string
  1856.  * Accepts: destination string to write
  1857.  *        message cache entry
  1858.  */
  1859.  
  1860. void bezerk_update_status (status,elt)
  1861.     char *status;
  1862.     MESSAGECACHE *elt;
  1863. {
  1864.   /* This used to be an sprintf(), but thanks to certain cretinous C libraries
  1865.      with horribly slow implementations of sprintf() I had to change it to this
  1866.      mess.  At least it should be fast. */
  1867.   char *t = status + 8;
  1868.   status[0] = 'S'; status[1] = 't'; status[2] = 'a'; status[3] = 't';
  1869.   status[4] = 'u'; status[5] = 's'; status[6] = ':';  status[7] = ' ';
  1870.   if (elt->seen) *t++ = 'R'; *t++ = 'O'; *t++ = '\n';
  1871.   *t++ = 'X'; *t++ = '-'; *t++ = 'S'; *t++ = 't'; *t++ = 'a'; *t++ = 't';
  1872.   *t++ = 'u'; *t++ = 's'; *t++ = ':'; *t++ = ' ';
  1873.   if (elt->deleted) *t++ = 'D'; if (elt->flagged) *t++ = 'F';
  1874.   if (elt->answered) *t++ = 'A';
  1875.   *t++ = '\n'; *t++ = '\n'; *t++ = '\0';
  1876. }
  1877.  
  1878. /* Parse flag list
  1879.  * Accepts: MAIL stream
  1880.  *        flag list as a character string
  1881.  * Returns: flag command list
  1882.  */
  1883.  
  1884.  
  1885. short bezerk_getflags (stream,flag)
  1886.     MAILSTREAM *stream;
  1887.     char *flag;
  1888. {
  1889.   char *t;
  1890.   short f = 0;
  1891.   short i,j;
  1892.   if (flag && *flag) {        /* no-op if no flag string */
  1893.                 /* check if a list and make sure valid */
  1894.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1895.       mm_log ("Bad flag list",ERROR);
  1896.       return NIL;
  1897.     }
  1898.                 /* copy the flag string w/o list construct */
  1899.     strncpy (LOCAL->buf,flag+i,(j = strlen (flag) - (2*i)));
  1900.     LOCAL->buf[j] = '\0';
  1901.     t = ucase (LOCAL->buf);    /* uppercase only from now on */
  1902.  
  1903.     while (*t) {        /* parse the flags */
  1904.       if (*t == '\\') {        /* system flag? */
  1905.     switch (*++t) {        /* dispatch based on first character */
  1906.     case 'S':        /* possible \Seen flag */
  1907.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1908.       t += 4;        /* skip past flag name */
  1909.       break;
  1910.     case 'D':        /* possible \Deleted flag */
  1911.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1912.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1913.       t += 7;        /* skip past flag name */
  1914.       break;
  1915.     case 'F':        /* possible \Flagged flag */
  1916.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1917.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1918.       t += 7;        /* skip past flag name */
  1919.       break;
  1920.     case 'A':        /* possible \Answered flag */
  1921.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1922.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1923.       t += 8;        /* skip past flag name */
  1924.       break;
  1925.     default:        /* unknown */
  1926.       i = 0;
  1927.       break;
  1928.     }
  1929.                 /* add flag to flags list */
  1930.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1931.     else {            /* bitch about bogus flag */
  1932.       mm_log ("Unknown system flag",ERROR);
  1933.       return NIL;
  1934.     }
  1935.       }
  1936.       else {            /* no user flags yet */
  1937.     mm_log ("Unknown flag",ERROR);
  1938.     return NIL;
  1939.       }
  1940.     }
  1941.   }
  1942.   return f;
  1943. }
  1944.  
  1945. /* Search support routines
  1946.  * Accepts: MAIL stream
  1947.  *        message number
  1948.  *        pointer to additional data
  1949.  * Returns: T if search matches, else NIL
  1950.  */
  1951.  
  1952.  
  1953. char bezerk_search_all (stream,msgno,d,n)
  1954.     MAILSTREAM *stream;
  1955.     long msgno;
  1956.     char *d;
  1957.     long n;
  1958. {
  1959.   return T;            /* ALL always succeeds */
  1960. }
  1961.  
  1962.  
  1963. char bezerk_search_answered (stream,msgno,d,n)
  1964.     MAILSTREAM *stream;
  1965.     long msgno;
  1966.     char *d;
  1967.     long n;
  1968. {
  1969.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1970. }
  1971.  
  1972.  
  1973. char bezerk_search_deleted (stream,msgno,d,n)
  1974.     MAILSTREAM *stream;
  1975.     long msgno;
  1976.     char *d;
  1977.     long n;
  1978. {
  1979.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1980. }
  1981.  
  1982.  
  1983. char bezerk_search_flagged (stream,msgno,d,n)
  1984.     MAILSTREAM *stream;
  1985.     long msgno;
  1986.     char *d;
  1987.     long n;
  1988. {
  1989.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1990. }
  1991.  
  1992.  
  1993. char bezerk_search_keyword (stream,msgno,d,n)
  1994.     MAILSTREAM *stream;
  1995.     long msgno;
  1996.     char *d;
  1997.     long n;
  1998. {
  1999.   return NIL;            /* keywords not supported yet */
  2000. }
  2001.  
  2002.  
  2003. char bezerk_search_new (stream,msgno,d,n)
  2004.     MAILSTREAM *stream;
  2005.     long msgno;
  2006.     char *d;
  2007.     long n;
  2008. {
  2009.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2010.   return (elt->recent && !elt->seen) ? T : NIL;
  2011. }
  2012.  
  2013. char bezerk_search_old (stream,msgno,d,n)
  2014.     MAILSTREAM *stream;
  2015.     long msgno;
  2016.     char *d;
  2017.     long n;
  2018. {
  2019.   return mail_elt (stream,msgno)->recent ? NIL : T;
  2020. }
  2021.  
  2022.  
  2023. char bezerk_search_recent (stream,msgno,d,n)
  2024.     MAILSTREAM *stream;
  2025.     long msgno;
  2026.     char *d;
  2027.     long n;
  2028. {
  2029.   return mail_elt (stream,msgno)->recent ? T : NIL;
  2030. }
  2031.  
  2032.  
  2033. char bezerk_search_seen (stream,msgno,d,n)
  2034.     MAILSTREAM *stream;
  2035.     long msgno;
  2036.     char *d;
  2037.     long n;
  2038. {
  2039.   return mail_elt (stream,msgno)->seen ? T : NIL;
  2040. }
  2041.  
  2042.  
  2043. char bezerk_search_unanswered (stream,msgno,d,n)
  2044.     MAILSTREAM *stream;
  2045.     long msgno;
  2046.     char *d;
  2047.     long n;
  2048. {
  2049.   return mail_elt (stream,msgno)->answered ? NIL : T;
  2050. }
  2051.  
  2052.  
  2053. char bezerk_search_undeleted (stream,msgno,d,n)
  2054.     MAILSTREAM *stream;
  2055.     long msgno;
  2056.     char *d;
  2057.     long n;
  2058. {
  2059.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  2060. }
  2061.  
  2062.  
  2063. char bezerk_search_unflagged (stream,msgno,d,n)
  2064.     MAILSTREAM *stream;
  2065.     long msgno;
  2066.     char *d;
  2067.     long n;
  2068. {
  2069.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  2070. }
  2071.  
  2072.  
  2073. char bezerk_search_unkeyword (stream,msgno,d,n)
  2074.     MAILSTREAM *stream;
  2075.     long msgno;
  2076.     char *d;
  2077.     long n;
  2078. {
  2079.   return T;            /* keywords not supported yet */
  2080. }
  2081.  
  2082.  
  2083. char bezerk_search_unseen (stream,msgno,d,n)
  2084.     MAILSTREAM *stream;
  2085.     long msgno;
  2086.     char *d;
  2087.     long n;
  2088. {
  2089.   return mail_elt (stream,msgno)->seen ? NIL : T;
  2090. }
  2091.  
  2092. char bezerk_search_before (stream,msgno,d,n)
  2093.     MAILSTREAM *stream;
  2094.     long msgno;
  2095.     char *d;
  2096.     long n;
  2097. {
  2098.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2099.   return (char) ((long) ((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  2100. }
  2101.  
  2102.  
  2103. char bezerk_search_on (stream,msgno,d,n)
  2104.     MAILSTREAM *stream;
  2105.     long msgno;
  2106.     char *d;
  2107.     long n;
  2108. {
  2109.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2110.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  2111. }
  2112.  
  2113.  
  2114. char bezerk_search_since (stream,msgno,d,n)
  2115.     MAILSTREAM *stream;
  2116.     long msgno;
  2117.     char *d;
  2118.     long n;
  2119. {
  2120.                 /* everybody interprets "since" as .GE. */
  2121.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  2122.   return (char)((long) ((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  2123. }
  2124.  
  2125.  
  2126. char bezerk_search_body (stream,msgno,d,n)
  2127.     MAILSTREAM *stream;
  2128.     long msgno;
  2129.     char *d;
  2130.     long n;
  2131. {
  2132.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  2133.   return search (m->body,m->bodysize,d,n);
  2134. }
  2135.  
  2136.  
  2137. char bezerk_search_subject (stream,msgno,d,n)
  2138.     MAILSTREAM *stream;
  2139.     long msgno;
  2140.     char *d;
  2141.     long n;
  2142. {
  2143.   char *s = bezerk_fetchstructure (stream,msgno,NIL)->subject;
  2144.   return s ? search (s,strlen (s),d,n) : NIL;
  2145. }
  2146.  
  2147.  
  2148. char bezerk_search_text (stream,msgno,d,n)
  2149.     MAILSTREAM *stream;
  2150.     long msgno;
  2151.     char *d;
  2152.     long n;
  2153. {
  2154.   FILECACHE *m = LOCAL->msgs[msgno - 1];
  2155.   return search (m->header,m->headersize,d,n) ||
  2156.     bezerk_search_body (stream,msgno,d,n);
  2157. }
  2158.  
  2159. char bezerk_search_bcc (stream,msgno,d,n)
  2160.     MAILSTREAM *stream;
  2161.     long msgno;
  2162.     char *d;
  2163.     long n;
  2164. {
  2165.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2166.                 /* get text for address */
  2167.   rfc822_write_address (LOCAL->buf,
  2168.             bezerk_fetchstructure (stream,msgno,NIL)->bcc);
  2169.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  2170. }
  2171.  
  2172.  
  2173. char bezerk_search_cc (stream,msgno,d,n)
  2174.     MAILSTREAM *stream;
  2175.     long msgno;
  2176.     char *d;
  2177.     long n;
  2178. {
  2179.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2180.                 /* get text for address */
  2181.   rfc822_write_address (LOCAL->buf,
  2182.             bezerk_fetchstructure (stream,msgno,NIL)->cc);
  2183.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  2184. }
  2185.  
  2186.  
  2187. char bezerk_search_from (stream,m,d,n)
  2188.     MAILSTREAM *stream;
  2189.     long m;
  2190.     char *d;
  2191.     long n;
  2192. {
  2193.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2194.                 /* get text for address */
  2195.   rfc822_write_address (LOCAL->buf,bezerk_fetchstructure (stream,m,NIL)->from);
  2196.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  2197. }
  2198.  
  2199.  
  2200. char bezerk_search_to (stream,msgno,d,n)
  2201.     MAILSTREAM *stream;
  2202.     long msgno;
  2203.     char *d;
  2204.     long n;
  2205. {
  2206.   LOCAL->buf[0] = '\0';        /* initially empty string */
  2207.                 /* get text for address */
  2208.   rfc822_write_address (LOCAL->buf,
  2209.             bezerk_fetchstructure (stream,msgno,NIL)->to);
  2210.   return search (LOCAL->buf,strlen (LOCAL->buf),d,n);
  2211. }
  2212.  
  2213. /* Search parsers */
  2214.  
  2215.  
  2216. /* Parse a date
  2217.  * Accepts: function to return
  2218.  *        pointer to date integer to return
  2219.  * Returns: function to return
  2220.  */
  2221.  
  2222. search_t bezerk_search_date (f,n)
  2223.     search_t f;
  2224.     long *n;
  2225. {
  2226.   long i;
  2227.   char *s;
  2228.   MESSAGECACHE elt;
  2229.                 /* parse the date and return fn if OK */
  2230.   return (bezerk_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  2231.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  2232. }
  2233.  
  2234. /* Parse a flag
  2235.  * Accepts: function to return
  2236.  *        pointer to string to return
  2237.  * Returns: function to return
  2238.  */
  2239.  
  2240. search_t bezerk_search_flag (f,d)
  2241.     search_t f;
  2242.     char **d;
  2243. {
  2244.                 /* get a keyword, return if OK */
  2245.   return (*d = strtok (NIL," ")) ? f : NIL;
  2246. }
  2247.  
  2248.  
  2249. /* Parse a string
  2250.  * Accepts: function to return
  2251.  *        pointer to string to return
  2252.  *        pointer to string length to return
  2253.  * Returns: function to return
  2254.  */
  2255.  
  2256.  
  2257. search_t bezerk_search_string (f,d,n)
  2258.     search_t f;
  2259.     char **d;
  2260.     long *n;
  2261. {
  2262.   char *c = strtok (NIL,"");    /* remainder of criteria */
  2263.   if (c) {            /* better be an argument */
  2264.     switch (*c) {        /* see what the argument is */
  2265.     case '\0':            /* catch bogons */
  2266.     case ' ':
  2267.       return NIL;
  2268.     case '"':            /* quoted string */
  2269.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  2270.     return NIL;
  2271.       break;
  2272.     case '{':            /* literal string */
  2273.       *n = strtol (c+1,&c,10);    /* get its length */
  2274.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  2275.       *n > strlen (*d = c)) return NIL;
  2276.       c[*n] = '\255';        /* write new delimiter */
  2277.       strtok (c,"\255");    /* reset the strtok mechanism */
  2278.       break;
  2279.     default:            /* atomic string */
  2280.       *n = strlen (*d = strtok (c," "));
  2281.       break;
  2282.     }
  2283.     return f;
  2284.   }
  2285.   else return NIL;
  2286. }
  2287.